#include <string>
#include <iostream>
#include <cmath> 

// For terminal delay
#include <chrono>
#include <thread>

#include <fstream>
#include <algorithm> 

#include "game.h"

Game::Game()
{
    mAudioInitialized = false;
    mPaused = false; 
    if(SDL_Init(SDL_INIT_AUDIO) < 0) {
        std::cerr << "SDL audio init failed: " << SDL_GetError() << std::endl;
    } else {
        if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) {
            std::cerr << "Mixer init failed: " << Mix_GetError() << std::endl;
        } else {

            mBgMusic = Mix_LoadMUS("sounds/bg_music.mp3");
            mEatSound = Mix_LoadWAV("sounds/eat_food.WAV");
            mCollisionSound = Mix_LoadWAV("sounds/collision.WAV");
            mGameOverSound = Mix_LoadWAV("sounds/game_over.wav");
            mTurnSound = Mix_LoadWAV("sounds/turn.WAV");
            mDashSound = Mix_LoadWAV("sounds/dash.WAV");
            if(!mBgMusic || !mEatSound || !mCollisionSound || !mGameOverSound||!mTurnSound||!mDashSound) {
                std::cerr << "Failed to load audio files: " << Mix_GetError() << std::endl;
            } else {
                mAudioInitialized = true;
                Mix_PlayMusic(mBgMusic, -1); // -1 for infinite loop
                Mix_VolumeMusic(MIX_MAX_VOLUME / 2); // Set volume to half
            }
        }
    }
    // Separate the screen to three windows
    this->mWindows.resize(3);
    setlocale(LC_ALL, "");
    initscr();
    // If there wasn't any key pressed don't wait for keypress
    nodelay(stdscr, true);
    // Turn on keypad control
    keypad(stdscr, true);
    // No echo for the key pressed
    noecho();
    // No cursor show
    curs_set(0);
    // Get screen and board parameters

     // 初始化颜色
    start_color();
    init_pair(1, COLOR_RED, COLOR_BLACK);  // 障碍
    init_pair(2, COLOR_GREEN, COLOR_BLACK);    // 食物
    init_pair(3, COLOR_YELLOW, COLOR_BLACK);   // 蛇身
    init_pair(4, COLOR_MAGENTA, COLOR_BLACK);
    init_pair(5, COLOR_BLUE, COLOR_BLACK);     //bash蛇身


    // Initialize leaderBoard
    this->mLeaderBoards.resize(2); 
    for (int mode = 0; mode < 2; mode++) {
        this->mLeaderBoards[mode].resize(3); 
        for (int speed = 0; speed < 3; speed++) {
            mLeaderBoards[mode][speed].assign(mNumLeaders, 0); 
        }
    }
    mCurrentSpeed = 1; 

    getmaxyx(stdscr, this->mScreenHeight, this->mScreenWidth);
    this->mGameBoardWidth = this->mScreenWidth - this->mInstructionWidth;
    this->mGameBoardHeight = this->mScreenHeight - this->mInformationHeight;

    this->createInformationBoard();
    this->createGameBoard();
    this->createInstructionBoard();

    // Initialize the leader board to be all zeros
    this->mLeaderBoard_Normal.assign(this->mNumLeaders, 0);
    this->mLeaderBoard_Timed.assign(this->mNumLeaders, 0);
    
}

Game::~Game()
{
    if(mAudioInitialized) {
        Mix_HaltMusic();
        Mix_FreeMusic(mBgMusic);
        Mix_FreeChunk(mEatSound);
        Mix_FreeChunk(mCollisionSound);
        Mix_FreeChunk(mGameOverSound);
        Mix_FreeChunk(mTurnSound);
        Mix_FreeChunk(mDashSound);
        Mix_CloseAudio();
        SDL_Quit();
    }
    for (int i = 0; i < this->mWindows.size(); i ++)
    {
        delwin(this->mWindows[i]);
    }
    endwin();
}

void Game::createInformationBoard()
{
    int startY = 0;
    int startX = 0;
    this->mWindows[0] = newwin(this->mInformationHeight, this->mScreenWidth, startY, startX);
}

void Game::renderInformationBoard() const
{
    mvwprintw(this->mWindows[0], 1, 1, "Welcome to The Snake Game!");
    mvwprintw(this->mWindows[0], 2, 1, "Author: Xiong Jiaxin  Fu Minghao  Tang Kaifeng");
    mvwprintw(this->mWindows[0], 3, 1, "Website: https://gitee.com/xjx20061004/snakegame_-guizhou.git");
    mvwprintw(this->mWindows[0], 4, 1, "Implemented using C++ and libncurses library.");
    wrefresh(this->mWindows[0]);
}

void Game::createGameBoard()
{
    int startY = this->mInformationHeight;
    int startX = 0;
    this->mWindows[1] = newwin(this->mScreenHeight - this->mInformationHeight, this->mScreenWidth - this->mInstructionWidth, startY, startX);
}

void Game::renderGameBoard() const
{
    wrefresh(this->mWindows[1]);
}

void Game::createInstructionBoard()
{
    int startY = this->mInformationHeight;
    int startX = this->mScreenWidth - this->mInstructionWidth;
    this->mWindows[2] = newwin(this->mScreenHeight - this->mInformationHeight, this->mInstructionWidth, startY, startX);
}

void Game::renderInstructionBoard() const
{
    mvwprintw(this->mWindows[2], 1, 1, "Manual");

    mvwprintw(this->mWindows[2], 3, 1, "Up: W");
    mvwprintw(this->mWindows[2], 4, 1, "Down: S");
    mvwprintw(this->mWindows[2], 5, 1, "Left: A");
    mvwprintw(this->mWindows[2], 6, 1, "Right: D");
    mvwprintw(this->mWindows[2], 8, 1, "Pause: P"); 

    mvwprintw(this->mWindows[2], 10, 1, "Difficulty");

    mvwprintw(this->mWindows[2], 13, 1, "Points");
    mvwprintw(this->mWindows[2], 16, 1, "Lives");



    wrefresh(this->mWindows[2]);
}

void Game::renderLives() const 
{
    std::string livesString = std::to_string(mLives) + "/" + std::to_string(mMaxLives);
    mvwprintw(this->mWindows[2], 17, 1, " ");
    mvwprintw(this->mWindows[2], 17, 1, livesString.c_str());
    wrefresh(this->mWindows[2]);
}


void Game::renderLeaderBoard() const
{
    // 检查是否有足够空间显示
    if (mScreenHeight - mInformationHeight - 24 < 3 * 2) {
        return;
    }

    int modeIndex = (mode == Mode::Normal) ? 0 : 1;
    const auto& currentBoard = mLeaderBoards[modeIndex][mCurrentSpeed]; // 当前排行榜数据

    // 显示标题（包含模式和速度）
    std::string title;
    if (mode == Mode::Normal) {
        title = "N "; // 普通模式标识
    } else {
        title = "T "; // 限时模式标识
    }
    // 追加速度标识
    switch (mCurrentSpeed) {
        case 0: title += "Slow Leader"; break;
        case 1: title += "Medium Leader"; break;
        case 2: title += "Fast Leader"; break;
    }
    mvwprintw(mWindows[2], 23, 1, title.c_str());

    // 显示排行榜数据（3个排名）
    std::string pointString;
    std::string rank;
    for (int i = 0; i < mNumLeaders; ++i) {
        pointString = std::to_string(currentBoard[i]);
        rank = "#" + std::to_string(i + 1) + ":";
        mvwprintw(mWindows[2], 24 + i, 1, rank.c_str()); // 排名行
        mvwprintw(mWindows[2], 24 + i, 5, pointString.c_str()); // 分数行
        
    }
    wrefresh(mWindows[2]);
}


int Game::renderMainMenu() const
{
    WINDOW * menu;
    int width = this->mGameBoardWidth * 0.5;
    int height = this->mGameBoardHeight * 0.5;
    int startX = this->mGameBoardWidth * 0.25;
    int startY = this->mGameBoardHeight * 0.25 + this->mInformationHeight;

    menu = newwin(height, width, startY, startX);
    box(menu, 0, 0);
    std::vector<std::string> menuItems = {"Normal Mode","Time-Limited Mode","Help","Rank","Quit"};

    int index = 0;
    int offset = 4;
    
    mvwprintw(menu,1,10,"Welcome to snakegame!");
    wattron(menu, A_STANDOUT);
    mvwprintw(menu, 0 + offset, 1, menuItems[0].c_str());
    wattroff(menu, A_STANDOUT);
    mvwprintw(menu, 1 + offset, 1, menuItems[1].c_str());
    wattroff(menu, A_STANDOUT);
    mvwprintw(menu, 2 + offset, 1, menuItems[2].c_str());
    wattroff(menu, A_STANDOUT);
    mvwprintw(menu, 3 + offset, 1, menuItems[3].c_str());
    wattroff(menu, A_STANDOUT);
    mvwprintw(menu, 4 + offset, 1, menuItems[4].c_str());



    wrefresh(menu);

    int key;
    while (true)
    {
        key = getch();
        switch(key)
        {
            case 'W':
            case 'w':
            case KEY_UP:
            {
                mvwprintw(menu, index + offset, 1, menuItems[index].c_str());
                index --;
                index = (index < 0) ? menuItems.size() - 1 : index;
                wattron(menu, A_STANDOUT);
                mvwprintw(menu, index + offset, 1, menuItems[index].c_str());
                wattroff(menu, A_STANDOUT);
                break;
            }
            case 'S':
            case 's':
            case KEY_DOWN:
            {
                mvwprintw(menu, index + offset, 1, menuItems[index].c_str());
                index ++;
                index = (index > menuItems.size() - 1) ? 0 : index;
                wattron(menu, A_STANDOUT);
                mvwprintw(menu, index + offset, 1, menuItems[index].c_str());
                wattroff(menu, A_STANDOUT);
                break;
            }
        }
        wrefresh(menu);
        if (key == ' ' || key == 10)
        {
            break;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    delwin(menu);

    return index;
    
}

// Add a menu to choose speed.
int Game::renderSpeedMenu() const {
    WINDOW* menu; 
    int width = this->mGameBoardWidth*0.5; 
    int height = this->mGameBoardHeight * 0.5; 
    int startX = this->mGameBoardWidth * 0.25; 
    int startY = mGameBoardHeight * 0.25 + this->mInformationHeight; 

    menu = newwin(height, width, startY, startX); 
    box(menu, 0, 0); 

    std::vector<std::string> menuItems = {"Slow", "Medium", "Fast"}; 

    int selectedIndex = 0 ;
    int offset = 4; 

    mvwprintw(menu,1,10,"Select Speed Level: ");
    wattron(menu, A_STANDOUT);
    mvwprintw(menu, 0 + offset, 1, menuItems[0].c_str());
    wattroff(menu, A_STANDOUT);
    mvwprintw(menu, 1 + offset, 1, menuItems[1].c_str());
    wattroff(menu, A_STANDOUT);
    mvwprintw(menu, 2 + offset, 1, menuItems[2].c_str());

    wrefresh(menu); 

    int key;
    while (true)
    {
        key = getch();
        switch(key)
        {
            case 'W':
            case 'w':
            case KEY_UP:
            {
                mvwprintw(menu, selectedIndex + offset, 1, menuItems[selectedIndex].c_str());
                selectedIndex--;
                selectedIndex = (selectedIndex < 0) ? menuItems.size() - 1 : selectedIndex;
                wattron(menu, A_STANDOUT);
                mvwprintw(menu, selectedIndex + offset, 1, menuItems[selectedIndex].c_str());
                wattroff(menu, A_STANDOUT);
                break;
            }
            case 'S':
            case 's':
            case KEY_DOWN:
            {
                mvwprintw(menu, selectedIndex + offset, 1, menuItems[selectedIndex].c_str());
                selectedIndex++;
                selectedIndex = (selectedIndex > menuItems.size() - 1) ? 0 : selectedIndex;
                wattron(menu, A_STANDOUT);
                mvwprintw(menu, selectedIndex + offset, 1, menuItems[selectedIndex].c_str());
                wattroff(menu, A_STANDOUT);
                break;
            }
        }
        wrefresh(menu);
        if (key == ' ' || key == 10)
        {
            break;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    delwin(menu);

    return selectedIndex;

}

int Game::renderPauseMenu() const {
    WINDOW *menu; 
    int width = this->mGameBoardWidth*0.5; 
    int height = this->mGameBoardHeight * 0.5; 
    int startX = this->mGameBoardWidth * 0.25; 
    int startY = mGameBoardHeight * 0.25 + this->mInformationHeight; 

    menu = newwin(height, width, startY, startX); 
    box(menu, 0, 0); 

    std::vector<std::string> menuItems = {"Continue", "Quit"}; 

    int index = 0 ;
    int offset = 4; 

    mvwprintw(menu,1,10,"Game paused. ");
    wattron(menu, A_STANDOUT);
    mvwprintw(menu, 0 + offset, 1, menuItems[0].c_str());
    wattroff(menu, A_STANDOUT);
    mvwprintw(menu, 1 + offset, 1, menuItems[1].c_str());

    wrefresh(menu); 

    int key;
    while (true)
    {
        key = getch();
        switch(key)
        {
            case 'W':
            case 'w':
            case KEY_UP:
            {
                mvwprintw(menu, index + offset, 1, menuItems[index].c_str());
                index --;
                index = (index < 0) ? menuItems.size() - 1 : index;
                wattron(menu, A_STANDOUT);
                mvwprintw(menu, index + offset, 1, menuItems[index].c_str());
                wattroff(menu, A_STANDOUT);
                break;
            }
            case 'S':
            case 's':
            case KEY_DOWN:
            {
                mvwprintw(menu, index + offset, 1, menuItems[index].c_str());
                index ++;
                index = (index > menuItems.size() - 1) ? 0 : index;
                wattron(menu, A_STANDOUT);
                mvwprintw(menu, index + offset, 1, menuItems[index].c_str());
                wattroff(menu, A_STANDOUT);
                break;
            }
        }
        wrefresh(menu);
        if (key == ' ' || key == 10)
        {
            break;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    delwin(menu);

    return index;

}



bool Game::renderRestartMenu() const
{
    WINDOW * menu;
    int width = this->mGameBoardWidth * 0.5;
    int height = this->mGameBoardHeight * 0.5;
    int startX = this->mGameBoardWidth * 0.25;
    int startY = this->mGameBoardHeight * 0.25 + this->mInformationHeight;

    menu = newwin(height, width, startY, startX);
    box(menu, 0, 0);
    std::vector<std::string> menuItems = {"Restart", "Quit"};

    int index = 0;
    int offset = 4;
    mvwprintw(menu, 1, 1, "Your Final Score:");
    std::string pointString = std::to_string(this->mPoints);
    mvwprintw(menu, 2, 1, pointString.c_str());
    wattron(menu, A_STANDOUT);
    mvwprintw(menu, 0 + offset, 1, menuItems[0].c_str());
    wattroff(menu, A_STANDOUT);
    mvwprintw(menu, 1 + offset, 1, menuItems[1].c_str());

    wrefresh(menu);

    int key;
    while (true)
    {
        key = getch();
        switch(key)
        {
            case 'W':
            case 'w':
            case KEY_UP:
            {
                mvwprintw(menu, index + offset, 1, menuItems[index].c_str());
                index --;
                index = (index < 0) ? menuItems.size() - 1 : index;
                wattron(menu, A_STANDOUT);
                mvwprintw(menu, index + offset, 1, menuItems[index].c_str());
                wattroff(menu, A_STANDOUT);
                break;
            }
            case 'S':
            case 's':
            case KEY_DOWN:
            {
                mvwprintw(menu, index + offset, 1, menuItems[index].c_str());
                index ++;
                index = (index > menuItems.size() - 1) ? 0 : index;
                wattron(menu, A_STANDOUT);
                mvwprintw(menu, index + offset, 1, menuItems[index].c_str());
                wattroff(menu, A_STANDOUT);
                break;
            }
        }
        wrefresh(menu);
        if (key == ' ' || key == 10)
        {
            break;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    delwin(menu);

    if (index == 0)
    {
        return true;
    }
    else
    {
        return false;
    }
    
}




void Game::renderPoints() const
{
    std::string pointString = std::to_string(this->mPoints);
    mvwprintw(this->mWindows[2], 14, 1, " ");
    mvwprintw(this->mWindows[2], 14, 1, pointString.c_str());
    wrefresh(this->mWindows[2]);
}

void Game::renderDifficulty() const
{
    std::string difficultyString = std::to_string(this->mDifficulty);
    mvwprintw(this->mWindows[2], 11, 1, difficultyString.c_str());
    wrefresh(this->mWindows[2]);
}

void Game::initializeGame()
{
    this->mPtrSnake.reset(new Snake(this->mGameBoardWidth, this->mGameBoardHeight, this->mInitialSnakeLength));
    this->createRamdonFood();
    this->initializeObstacles(); 
    this->mPtrSnake->senseFood(this->mFood);
    this->mDifficulty = 0;
    this->mPoints = 0;
    this->mLives = mMaxLives;
    this->mDelay = this->mBaseDelay;
    this->mOriginalDelay = this->mBaseDelay;
    this->mIsSlowing = false;
    this->mRunning = true;
    this->mPaused = false; 
    this->state = State::Lived;
    if (mode == Mode::TimeLimited)
    {
        if (speedmode == SpeedMode::Slow)
        {
            mBaseDelay = 200;mRemainingTime = 25;
        }

        else if (speedmode == SpeedMode::Medium)
        {
            mBaseDelay = 150; mRemainingTime = 20;
        }

        else
        {
            mBaseDelay = 100; mRemainingTime = 15;
        }
        this->mTimerThread = std::thread(&Game::countDown, this);
    }
}

void Game::createRamdonFood()
{
    std::vector<SnakeBody> availableGrids;
    for (int i = 3; i < this->mGameBoardHeight - 3; i ++)
    {
        for (int j = 3; j < this->mGameBoardWidth - 3; j ++)
        {
            if(this->mPtrSnake->isPartOfSnake(j, i) || this->isObstacleAt(j ,i) )
            {
                continue;
            }
            else
            {
                availableGrids.push_back(SnakeBody(j, i));
            }
        }
    }

    // Randomly select a grid that is not occupied by the snake
    int random_idx = std::rand() % availableGrids.size();
    this->mFood = availableGrids[random_idx];
}

void Game::renderFood() const
{
    wattron(this->mWindows[1],COLOR_PAIR(2));
    mvwaddch(this->mWindows[1], this->mFood.getY(), this->mFood.getX(), this->mFoodSymbol);
    wattroff(this->mWindows[1],COLOR_PAIR(2));
    wrefresh(this->mWindows[1]);
}

void Game::renderSnake() const
{
    int snakeLength = this->mPtrSnake->getLength();
    std::vector<SnakeBody>& snake = this->mPtrSnake->getSnake();
    
    // 根据当前状态选择颜色
    short colorPair = 3; // 默认普通状态
    if (state == State::Invicible) {
        colorPair = 4; // 无敌状态
    } else if (state == State::Bash) {
        colorPair = 5; // 冲撞状态
    }
    
    // 应用选中的颜色绘制蛇
    wattron(this->mWindows[1], COLOR_PAIR(colorPair));
    for (int i = 0; i < snakeLength; i++) {
        mvwaddch(this->mWindows[1], snake[i].getY(), snake[i].getX(), this->mSnakeSymbol);
    }
    wattroff(this->mWindows[1], COLOR_PAIR(colorPair));
    
    wrefresh(this->mWindows[1]);
}

void Game::controlSnake() const
{
    int key = getch();
    bool directionChanged = false;
    Direction newDirection = mPtrSnake->getDirection(); // 获取当前方向

    switch(key)
    {
        case 'W':
        case 'w':
        case KEY_UP:
            newDirection = Direction::Up;
            break;
        
        case 'S':
        case 's':
        case KEY_DOWN:
            newDirection = Direction::Down;
            break;
        
        case 'A':
        case 'a':
        case KEY_LEFT:
            newDirection = Direction::Left;
            break;
        
        case 'D':
        case 'd':
        case KEY_RIGHT:
            newDirection = Direction::Right;
            break;

        case 'P':
        case 'p':
            const_cast<Game*>(this)->mPaused = !(const_cast<Game*>(this)->mPaused); 
            break; 
            
        case ' ':
            if (state == State::Invicible) break;
            const_cast<Game*>(this)->spacePressed = true;
            const_cast<Game*>(this)->mIsSpaceHeld = true;
            const_cast<Game*>(this)->state = State::Bash;
            if(mAudioInitialized && !Mix_Playing(2)) { 
                Mix_PlayChannel(2, mDashSound, 0); 
            }
            break;

        case ERR: 
            if (const_cast<Game*>(this)->mIsSpaceHeld) {
                const_cast<Game*>(this)->mIsSpaceHeld = false;
                const_cast<Game*>(this)->spacePressed = false;
                if (const_cast<Game*>(this)->state == State::Bash) {
                    const_cast<Game*>(this)->state = State::Lived;
                }
            }
            break;
            
        default:
            if (const_cast<Game*>(this)->mIsSpaceHeld) {
                const_cast<Game*>(this)->mIsSpaceHeld = false;
                const_cast<Game*>(this)->spacePressed = false;
                const_cast<Game*>(this)->state = State::Lived;
            }
    }

    // 统一处理方向改变和音效
    if (newDirection != mPtrSnake->getDirection()) {
        directionChanged = mPtrSnake->changeDirection(newDirection);
        if (directionChanged && mAudioInitialized) {
            Mix_PlayChannel(-1, mTurnSound, 0);
        }
    }

    // 状态更新
    if (directionChanged && !spacePressed && state != State::Invicible) {
        const_cast<Game*>(this)->state = State::Lived;
    }
}

void Game::renderBoards() const
{
    for (int i = 0; i < this->mWindows.size(); i ++)
    {
        werase(this->mWindows[i]);
    }
    this->renderInformationBoard();
    this->renderGameBoard();
    this->renderInstructionBoard();
    for (int i = 0; i < this->mWindows.size(); i ++)
    {
        box(this->mWindows[i], 0, 0);
        wrefresh(this->mWindows[i]);
    }
    this->renderLeaderBoard();
}


void Game::adjustDelay()
{
    this->mDifficulty = this->mPoints / 5;
    if (mPoints % 5 == 0)
    {
        this->mOriginalDelay = std::max(this->mBaseDelay * pow(0.8, this->mDifficulty),50.0);
        if (!mIsSlowing) {
            this->mDelay = this->mOriginalDelay;
        }
        int addObstacles = 10; 
        for (int i = 0; i < addObstacles; i++) {
            createRandomObstacle(); 
        }
    }
}

void Game::runGame()
{
    bool moveSuccess;
    int key;
    
    std::chrono::steady_clock::time_point invincibleStartTime;
    bool isInvincible = false;
    int invincibleDuration = 5000; // 5秒无敌时间
    
    while (true)
    {
        werase(this->mWindows[1]);
        box(this->mWindows[1], 0, 0);
        
        if (mPaused) {
            int pauseChoice = renderPauseMenu(); 
            if (pauseChoice == 1) {
                mRunning = false; 
                break; 
            } else {
                mPaused = false; 
                this->renderBoards(); 
            }
        }
        
        if (isInvincible) {
            auto now = std::chrono::steady_clock::now();
            auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - invincibleStartTime).count();
            if (elapsed >= invincibleDuration) {
                isInvincible = false;
                state = State::Lived;
            }
        }
        
        this->controlSnake();

        if (mIsSpaceHeld) {
            state = State::Bash;
        }
        
        // 处理减速状态
        if (mIsSlowing) {
            auto now = std::chrono::steady_clock::now();
            auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - mSlowStartTime).count();
            
            if (elapsed >= 5000) { // 5秒后恢复原速
                mDelay = mOriginalDelay;
                mIsSlowing = false;
            } else {
                // 线性插值恢复速度
                float progress = static_cast<float>(elapsed) / 5000.0f;
                mDelay = static_cast<int>(mOriginalDelay * (1.0f + (1.0f - progress)));
            }
        }
        
        // 根据状态调整延迟
        if (state == State::Bash) {
            mDelay = 40;
        } else {
            mDelay = mOriginalDelay;
        }
        
        bool eatFood = this->isFoodEaten();
        bool collision = this->mPtrSnake->checkCollision();
        bool obsCollision = this->isObstacleCollision(); 
        
        // 处理碰撞
        // 1. 先处理冲撞状态的障碍物碰撞
        bool shouldDie = false;
        if (state == State::Bash && obsCollision) {
            SnakeBody head = this->mPtrSnake->getSnake()[0];
            auto it = obstacles.begin();
            while (it != obstacles.end()) {
                if (it->first == head.getX() && it->second == head.getY()) {
                    if (this->mPtrSnake->getLength() <= 2) {
                        shouldDie = true;  // 长度≤2时直接触发死亡
                        break;
                    }
                    this->mPtrSnake->reduceLength();
                    break;  // 减少1节身体
                }
                else {
                    ++it;
                }
            }
        }

        // 2. 统一判断是否触发死亡逻辑
        if (collision) {
            shouldDie = true; 
        } else if (obsCollision && state == State::Lived) {
            shouldDie = true;
        }

        // 处理死亡逻辑
        if (shouldDie) {
            if (mAudioInitialized) {
                Mix_PlayChannel(-1, mCollisionSound, 0);
            }
            
            mLives--;
            this->renderLives();
            
            if (mLives <= 0) {
                if (mAudioInitialized) {
                    Mix_PlayChannel(-1, mGameOverSound, 0);
                }
                mRunning = false;
                break;
            } else {
                // 复活并进入无敌状态
                int lastLength = this->mPtrSnake->getLength();
                this->mPtrSnake.reset(new Snake(this->mGameBoardWidth, this->mGameBoardHeight, lastLength));
                this->mPtrSnake->senseFood(this->mFood);
                mIsSlowing = true;
                state = State::Invicible;
                isInvincible = true;
                invincibleStartTime = std::chrono::steady_clock::now();
                mDelay = mOriginalDelay * 2;  // 初始减速
                continue;
            }
        }       

        if (eatFood) {
            if(mAudioInitialized) {
                Mix_PlayChannel(-1, mEatSound, 0);
            }
            this->mPoints++;
            this->renderPoints();
            this->adjustDelay();
            this->mPtrSnake->grow();
            this->createRamdonFood(); 
            
            // 限时模式：吃到食物增加时间
            if (mode == Mode::TimeLimited) {
                int timeToAdd = 5; // 默认增加5秒
                {
                    std::lock_guard<std::mutex> lock(mMutex);
                    mRemainingTime += timeToAdd;
                    mUpdatePending = true;
                    mTimeUpdated = true;
                    updateTimeDisplay();
                }
            }
        } else {
            this->mPtrSnake->moveFoward(); 
        }
        
        this->renderFood();
        this->renderObstacles(); 
        this->renderDifficulty();
        this->renderPoints();
        this->renderLives();
        this->renderSnake();
        
        if (!mRunning) {
            break;
        }
        
        std::this_thread::sleep_for(std::chrono::milliseconds(this->mDelay));
        refresh();
    }
}

void Game::startGame()
{
    refresh();
    int choice1;
    bool choice2;
    while (true)
    {
        this->readLeaderBoard();
        this->renderBoards();
        choice1 = this->renderMainMenu(); //获取用户选择

        //若在主界面选择普通模式，进入普通模式
        if (choice1 == 0)
        {   
            mode = Mode::Normal;
            renderBoards();

            int speedChoice = renderSpeedMenu(); 
            mCurrentSpeed = speedChoice; 
            switch (speedChoice) {
                case 0: 
                    mBaseDelay = 200; break; 
                case 1: 
                    mBaseDelay = 150; break; 
                case 2: 
                    mBaseDelay = 100; break; 
                default: 
                    mBaseDelay = 150; mCurrentSpeed = 1; 
            }
            this->renderLeaderBoard(); 
            wrefresh(mWindows[2]); 

            mRunning = true;
            while(true)
            {
                this->initializeGame();
                this->runGame();
                this->updateLeaderBoard();
                this->writeLeaderBoard();
                this->renderBoards();
                choice2 = this->renderRestartMenu();

                //若在游戏结束界面选择退出，返回主界面
                if (choice2 == false)
                {
                    break;
                }
            }
        }

        //若在主界面选择限时模式，进入限时模式
        if (choice1 == 1)
        {
            mode = Mode::TimeLimited;
            renderBoards();

            int speedChoice = renderSpeedMenu(); 
            mCurrentSpeed = speedChoice; 
            switch (speedChoice) {
                case 0: 
                    mBaseDelay = 200;mRemainingTime = 30;speedmode = Slow;break; 
                    
                case 1: 
                    mBaseDelay = 150; mRemainingTime = 30;speedmode = Medium;break; 
                    
                case 2: 
                    mBaseDelay = 100; mRemainingTime = 30;speedmode = Fast;break; 
                    
                default: 
                    mBaseDelay = 150; mCurrentSpeed = 1; mRemainingTime = 30;
            }
            this->renderBoards(); 
            wrefresh(mWindows[2]); 


            mRunning = true;
            while(mRunning)
            {
                this->initializeGame();
                this->runGame();

                {
                    std::unique_lock<std::mutex> lock(mMutex);
                    if (mTimeUpdated) 
                    {
                        updateTimeDisplay();
                        mUpdatePending = false;
                    }       
                }

                this->updateLeaderBoard();
                this->writeLeaderBoard();
                this->renderBoards();
                choice2 = this->renderRestartMenu();
                if (mTimerThread.joinable()) 
                {
                    mTimerThread.join();
                }
                
                if (choice2 == true)
                {
                    mRunning = true;
                }

                //若在游戏结束界面选择退出，返回主界面
                if (choice2 == false)
                {
                    mRunning = false;
                    break;
                }
            }
        }

        if (choice1 == 2)
        {
            this->renderHelpBoard();
        }

        if (choice1 == 3)
        {
            this->renderRankBoard();
        }

        //若在主界面选择退出，直接退出游戏
        if (choice1 == 4)
        {
            break;
        }
    }
}
        

// https://en.cppreference.com/w/cpp/io/basic_fstream
bool Game::readLeaderBoard()
{
    // 读取普通模式排行榜
    for (int speed = 0; speed < 3; speed++) 
    {
        std::string filePath = this->mRecordBoardFilePath + "_normal_" + std::to_string(speed) + ".dat";
        std::fstream fhand(filePath, std::ios::binary | std::ios::in);
        
        if (fhand.is_open()) {
            int temp;
            int i = 0;
            while (i < mNumLeaders && fhand.read(reinterpret_cast<char*>(&temp), sizeof(temp))) {
                this->mLeaderBoards[0][speed][i] = temp;
                i++;
            }
            fhand.close();
        } else {
            // 文件不存在，初始化数组为0
            std::fill(mLeaderBoards[0][speed].begin(), mLeaderBoards[0][speed].end(), 0);
        }
    }
    
    // 读取限时模式排行榜
    for (int speed = 0; speed < 3; speed++)
    {
        std::string filePath = this->mRecordBoardFilePath + "_timed_" + std::to_string(speed) + ".dat";
        std::fstream fhand(filePath, std::ios::binary | std::ios::in);
        
        if (fhand.is_open()) {
            int temp;
            int i = 0;
            while (i < mNumLeaders && fhand.read(reinterpret_cast<char*>(&temp), sizeof(temp))) {
                this->mLeaderBoards[1][speed][i] = temp;
                i++;
            }
            fhand.close();
        } else {
            // 文件不存在，初始化数组为0
            std::fill(mLeaderBoards[1][speed].begin(), mLeaderBoards[1][speed].end(), 0);
        }
    }
    return true;
}


bool Game::updateLeaderBoard()
{
    bool updated = false;
    int newScore = mPoints;
    int modeIndex = (mode == Mode::Normal) ? 0 : 1; // 模式索引
    auto& currentBoard = mLeaderBoards[modeIndex][mCurrentSpeed]; // 当前速度对应的排行榜

    // 插入新分数，保持排行榜降序
    for (int i = 0; i < mNumLeaders; ++i) {
        if (currentBoard[i] >= newScore) {
            continue; // 分数不够，不更新
        }
        // 替换并后移旧分数
        int oldScore = currentBoard[i];
        currentBoard[i] = newScore;
        newScore = oldScore;
        updated = true;
    }

    return updated;
}

bool Game::writeLeaderBoard()
{
    int modeIndex = (mode == Mode::Normal) ? 0 : 1; // 模式索引（0/1）
    std::string basePath = (mode == Mode::Normal) 
        ? mRecordBoardFilePath + "_normal_" 
        : mRecordBoardFilePath + "_timed_";

    // 写入当前速度对应的排行榜文件（如record_normal_0.dat）
    std::string filePath = basePath + std::to_string(mCurrentSpeed) + ".dat";
    std::fstream fhand(filePath, std::ios::binary | std::ios::out | std::ios::trunc);

    if (!fhand.is_open()) {
        return false;
    }

    // 写入当前速度排行榜的3个数据
    for (int i = 0; i < mNumLeaders; ++i) {
        fhand.write(
            reinterpret_cast<char*>(&mLeaderBoards[modeIndex][mCurrentSpeed][i]),
            sizeof(mLeaderBoards[modeIndex][mCurrentSpeed][i])
        );
    }
    fhand.close();
    return true;
}

/*  methods of Obstacles
1. initializeObstacles()
2. createRandomObstacle()
3. isObstacleAt(int x, int y) const
4. isObstacleCollision() const; 
5. renderObstacles() const; 
*/
void Game::initializeObstacles() {
    obstacles.clear();
    srand(time(nullptr));

    maxObstacles = 10; 
    
    for (int i = 0; i < maxObstacles; i++) {
        createRandomObstacle();
    }
}

void Game::createRandomObstacle() {
    int x, y; 
    bool validPos; 

    do {
        validPos = true; 
        x = rand() % (this->mGameBoardWidth - 10) + 5; 
        y = rand() % (this->mGameBoardHeight - 10) + 5; 
        
        for (const auto& segment : this->mPtrSnake->getSnake()) {
            if (segment.getX() == x && segment.getY() == y) {
                validPos = false; 
                break; 
            }
        }

        if (validPos && this->mFood.getX() == x && this->mFood.getY() == y) {
            validPos = false; 
        }

        for (const auto& obs: this->obstacles) {
            if (obs.first == x && obs.second == y) {
                validPos = false; 
                break; 
            }
        }
    } while (!validPos); 

    obstacles.emplace_back(x, y); 
}

bool Game::isObstacleAt(int x, int y) const {
    for (const auto& obs: this->obstacles) {
        if (obs.first == x && obs.second == y) {
            return true; 
        }
    }
    return false; 
}

bool Game::isObstacleCollision() const {
    SnakeBody segment = this->mPtrSnake->getSnake()[0]; 
    if (isObstacleAt(segment.getX(), segment.getY())) {
        return true; 
    }
    return false; 
}

void Game::renderObstacles() const {
    for (const auto& obs : this->obstacles) {
        wattron(this->mWindows[1],COLOR_PAIR(1));
        mvwaddch(this->mWindows[1], obs.second, obs.first, this->obstacleSymbol);
        wattroff(this->mWindows[1],COLOR_PAIR(1));
    }
    wrefresh(this->mWindows[1]);
}



//methods of Time-limited mode
void Game::countDown()
{
        while (mRunning && mRemainingTime > 0) {
            if (mPaused) {
                std::this_thread::sleep_for(std::chrono::milliseconds(100)); 
                continue; 
            }

            std::this_thread::sleep_for(std::chrono::seconds(1));
            
            // 减少时间并更新显示
            {
                std::lock_guard<std::mutex> lock(mMutex);
                if (mRemainingTime > 0 && !mPaused) {
                    mRemainingTime--;
                    mUpdatePending = true;
                    mTimeUpdated = true;
                    updateTimeDisplay();
                }
            }
            mCv.notify_one();
        }

        if (mRemainingTime == 0)
        {
            if(mAudioInitialized) {
                Mix_PlayChannel(-1, mGameOverSound, 0);
            }
            mRunning = false;
        }
}

void Game::updateTimeDisplay() 
{
    // 添加标题和时间
    mvwprintw(mWindows[2], 19, 1, "Remaining time:");
    mvwprintw(mWindows[2], 20, 1,"%02d:%02d", 
            mRemainingTime / 60, mRemainingTime % 60);
}


//methods of help and rank board
void Game::renderHelpBoard() const
{
    WINDOW * menu;
    int width = this->mGameBoardWidth * 0.9;
    int height = this->mGameBoardHeight * 0.9;
    int startX = this->mGameBoardWidth * 0.08;
    int startY = this->mGameBoardHeight * 0.08 + this->mInformationHeight;

    menu = newwin(height, width, startY, startX);
    box(menu, 0, 0);
    std::vector<std::string> menuItems = {"Quit"};

    wattron(menu, A_STANDOUT);
    mvwprintw(menu,height-2, (width - menuItems[0].length()) / 2, menuItems[0].c_str());
    wattroff(menu, A_STANDOUT);
    wrefresh(menu);

    //增加这几个变量，方便整体移动
    int manual = 1;
    int normal = manual + 11;
    int timed = normal + 5;
    int speed = timed + 4;
    int invincibility = speed + 3;
    mvwprintw(menu, manual, 1, "Manual");
    mvwprintw(menu, manual + 2, 1, "Up: W");
    mvwprintw(menu, manual + 3, 1, "Down: S");
    mvwprintw(menu, manual + 4, 1, "Left: A");
    mvwprintw(menu, manual + 5, 1, "Right: D");
    mvwprintw(menu, manual + 6, 1, "Pause: P"); 
    mvwprintw(menu, manual + 2, 15, "Obstacle: X");
    mvwprintw(menu, manual + 3, 15, "Food: #");

    mvwprintw(menu, manual + 4, 15, "Bash and Accelerate: Press and hold Spacebar");
    mvwprintw(menu, manual + 5, 17, "When colliding, snakes can penetrate obstacles, reducing their length by 1.");
    wattron(menu,COLOR_PAIR(5));
    mvwprintw(menu, manual + 6, 17, "When you are bashing, the snake turns blue.");
    wattroff(menu,COLOR_PAIR(5));
    wattron(menu,COLOR_PAIR(1));
    mvwprintw(menu, manual + 7, 19, "!Caution: When the snake's length is reduced to 1, the snake dies.");
    mvwprintw(menu, manual + 8, 5, "When using bashing, do not hold down the space bar for too long, as this may cause the snake to become uncontrollable.");
    wattroff(menu,COLOR_PAIR(1));
    
    mvwprintw(menu, normal, 1, "Normal Mode");
    mvwprintw(menu, normal + 1, 3, "The snake dies when it collides with itself, a wall, or an obstacle.");
    mvwprintw(menu, normal + 2, 3,"When you eat food, the snake's body becomes 1 point longer, and the score increases by 1 point.");
    mvwprintw(menu, normal + 3, 3, "For every 5 points, the speed increases, while the number of obstacles increases by 10");
    mvwprintw(menu, timed, 1, "Time-limited Mode");
    mvwprintw(menu, timed + 1, 3, "On the basis of the normal mode, the time limit is 25(Slow)/20(Medium)/15(Fast) seconds.");
    mvwprintw(menu, timed + 2, 3, "When you eat food, time increases by 5.");

    mvwprintw(menu, speed, 1, "Speed Setting");
    mvwprintw(menu, speed + 1, 3, "Each mode has three speed settings, slow, medium, and fast, for you to choose from.");

    mvwprintw(menu, invincibility, 1, "Invincible State");
    mvwprintw(menu, invincibility + 1, 3, "Each time a snake revives, it gains five seconds of invincibility.");
    wattron(menu,COLOR_PAIR(4));
    mvwprintw(menu, invincibility + 2, 3, "When you are invincible, the snake turns purple.");
    wattroff(menu,COLOR_PAIR(4));
    wattron(menu,COLOR_PAIR(1));
    mvwprintw(menu, invincibility + 3, 5, "!Caution: Even in invincible state, colliding with oneself or the boundary will still result in death.");
    wattroff(menu,COLOR_PAIR(1));

    int key;
    while ((key = wgetch(menu)) != ' ' && key != 10) 
    {}
    delwin(menu);
}
    
void Game::renderRankBoard() const {
    WINDOW * menu;
    int width = this->mGameBoardWidth * 0.8; // 加宽窗口以显示更多内容
    int height = this->mGameBoardHeight * 0.8;
    int startX = this->mGameBoardWidth * 0.1;
    int startY = this->mGameBoardHeight * 0.1 + this->mInformationHeight;

    menu = newwin(height, width, startY, startX);
    box(menu, 0, 0);
    std::vector<std::string> menuItems = {"Quit"};

    // 底部退出按钮
    wattron(menu, A_STANDOUT);
    mvwprintw(menu, height-2, (width - menuItems[0].length()) / 2, menuItems[0].c_str());
    wattroff(menu, A_STANDOUT);

    // 标题
    mvwprintw(menu, 1, (width - 20)/2, "All Leader Boards");

    // 普通模式各速度排行榜（行1-10）
    mvwprintw(menu, 3, 2, "Normal Mode:");
    mvwprintw(menu, 4, 12, "Slow:");    // 普通模式慢速
    mvwprintw(menu, 4, 27, "Medium:");  // 普通模式中速
    mvwprintw(menu, 4, 42, "Fast:");    // 普通模式快速

    // 限时模式各速度排行榜（行12-21）
    mvwprintw(menu, 12, 2, "Time-Limited Mode:");
    mvwprintw(menu, 13, 12, "Slow:");   // 限时模式慢速
    mvwprintw(menu, 13, 27, "Medium:"); // 限时模式中速
    mvwprintw(menu, 13, 42, "Fast:");   // 限时模式快速

    // 填充数据
    std::string rank;
    // 普通模式（mode=0）
    for (int speed = 0; speed < 3; ++speed) {
        const auto& board = mLeaderBoards[0][speed]; // 普通模式+speed的排行榜
        for (int i = 0; i < mNumLeaders; ++i) {
            rank = "#" + std::to_string(i+1) + ":" + std::to_string(board[i]);
            mvwprintw(menu, 5 + i, 15 + speed*15, rank.c_str()); // 横向排列排名
        }
    }
    // 限时模式（mode=1）
    for (int speed = 0; speed < 3; ++speed) {
        const auto& board = mLeaderBoards[1][speed]; // 限时模式+speed的排行榜
        for (int i = 0; i < mNumLeaders; ++i) {
            rank = "#" + std::to_string(i+1) + ":" + std::to_string(board[i]);
            mvwprintw(menu, 14 + i, 15 + speed*15, rank.c_str()); // 横向排列排名
        }
    }

    wrefresh(menu);

    // 等待用户按空格或回车退出
    int key;
    while ((key = wgetch(menu)) != ' ' && key != 10) {}
    delwin(menu);
}

bool Game::isFoodEaten() const
{
    SnakeBody head = this->mPtrSnake->getSnake()[0];
    return (head.getX() == this->mFood.getX() && head.getY() == this->mFood.getY());
}